/**
 * iFramer - The IFrame Manipulator
 *
 * http://code.google.com/p/iframer
 *
 * Copyright:
 *     2009 Jose Badeau
 *
 * License:
 *     http://www.gnu.org/licenses/gpl.html
 *
 * Authors:
 *     Jose Badeau, jose.badeau@gmail.com
 *
 * version: 1.0
 *
 * Issues:
 *     Firefox and Safari (webkit) do not properly display the scrollWidth, clientWidth or innerWidth when an iframe's content width has decreases.
 *     This means that the resizeIFrames method can only resize if the content size increase but not if it decreases.
 *     This can be worked around by providing a resize hint.
 */
(function(jQuery) {

    // console for debugging
    // uncomment for release
    var console;

    /**
     * Resizes the selected iframes to the size of their contained content.
     *
     * The following settings can be overridden or defined with the options array:
     *     disableWidthResizing {Boolean} (false): enable or disable resizing of iframe widths
     *
     *     disableheightResizing {Boolean} (true): enables or disable resizing of iframe heights
     *
     *     resizeHints {Array} ([]): an array of jquery selector strings that are used to help resize specific
     *                               iframes if the default algorithm is unsatisfactory. The array format is:
     *                                   key {String}: the dom id of the iframe to resize
     *
     *                                   value {String}: the jquery selector string of a block element inside the iframe
     *                                                   that is used to calculate the iframe's size
     *
     * example:
     *     <code>
     *     var resizeHintz = new Array();
     *     resizeHintz["greenIFrame"] = "#greenDiv";
     *     resizeHintz["blueIFrame"] = ".hintable";
     *
     *     var customOptions = {
     *         resizeHints:resizeHintz
     *     };
     *
     *     // resize all iframes if needed every 1 millisecond
     *     $(document).ready(function () {
     *         setInterval(function() {
     *             $(".resizable").resizeIFrames(customOptions)
     *         }, 1);
     *     });
     *     </code>
     *
     * @param options {Array} an array of custom settings
     * @return {Array} an array of iframe dom object
     */
    jQuery.fn.resizeIFrames = function(options) {

        // define defaults and override with options, if available by extending the default settings, don't modify the
        // argument
        settings = jQuery.extend({
            disableWidthResizing: false,
            disableHeightResizing: false,
            resizeHints: new Array()
        }, options);

        // iterate and resize each iframe
        return this.each(function() {

            var $iframe = jQuery(this);

            if (console) {
                console.debug("[%s]", $iframe.attr("id"));
            }

            var resizeHintDimensions = _getResizeHint($iframe, settings);

            // resize width
            if (!$iframe.data("resizingDisabled")) {
                _resizeWidth($iframe, resizeHintDimensions);
            }

            // resize height
            if (!$iframe.data("resizingDisabled")) {
                _resizeHeigth($iframe, resizeHintDimensions);
            }

            // add blank line to help with log readability
            if (console) {
                console.debug(" ");
            }

        });

    };

    /**
     * Resizes the width of an iframe.
     *
     * @param $iframe {Object} an iframe dom object
     * @param resizeHint {Array} the resize hint for an iframe
     */
    function _resizeWidth($iframe, resizeHint) {

        var iframeWidth = $iframe.width();
        var contentWidth = resizeHint.width;

        try {

            if (contentWidth == -1) {
                if (document.all) {
                    contentWidth = $iframe.contents().find("body").attr("scrollWidth");
                } else {
                    contentWidth = $iframe.contents().find("html").attr("scrollWidth");
                }
            }

            if (console) {
                console.debug("iframe width: %i", iframeWidth);
                console.debug("content width: %i", contentWidth);
            }

            if (iframeWidth != contentWidth) {
                $iframe.width(contentWidth);
            }

        }
        catch(e) {
            if (console) {
                console.error("error updating width; skipping resizing");
                console.error("exception: %o", e);
            }
            $iframe.data('resizingDisabled', true);
        }
    };

    /**
     * Resizes the height of an iframe.
     *
     * @param $iframe {Object} an iframe dom object
     * @param resizeHint {Array} the resize hint for an iframe
     */
    function _resizeHeigth($iframe, resizeHint) {

        var iframeHeight = $iframe.height();
        var iframeContentHeight = resizeHint.height;

        try {

            if (iframeContentHeight == -1) {
                if (document.all) {
                    iframeContentHeight = $iframe.contents().find("body").attr("scrollHeight");
                } else {
                    iframeContentHeight = $iframe.contents().find("html").attr("scrollHeight");
                }
            }

            if (console) {
                console.debug("iframe height: %i", iframeHeight);
                console.debug("content height: %i", iframeContentHeight);
            }

            if (iframeHeight != iframeContentHeight) {
                $iframe.height(iframeContentHeight);
            }

        }
        catch(e) {
            if (console) {
                console.error("error updating height; skipping resizing");
                console.error("exception: %o", e);
            }
            $iframe.data('resizingDisabled', true);
        }
    };

    /**
     * Gets the resize hint (width and height) for an iframe;
     *
     * @param $iframe {Object} an iframe dom object
     * @param settings {Array} an array of custom settings
     * @return {Object} the resize hint for an iframe; -1 values if not found
     */
    function _getResizeHint($iframe, settings) {
        var resizeHint = {height:-1, width:-1};
        var resizeHints = settings.resizeHints;
        var iframeId = $iframe.attr("id");
        var resizeHintSelector = resizeHints[iframeId];
        if (resizeHintSelector) {
            try {
                if (resizeHintSelector != null) {
                    var $domElement = $iframe.contents().find(resizeHintSelector);
                    if ($domElement.length < 1) {
                        if (console) {
                            console.warn("resize hint '%s' for iframe '%s' did not return a dom element", resizeHintSelector, iframeId);
                        }
                    }
                    else if ($domElement.length > 1) {
                        if (console) {
                            console.warn("resize hint '%s' for iframe '%s' returned %s elements", resizeHintSelector, iframeId, $domElement.length);
                        }
                    }
                    else {
                        resizeHint = {width: $domElement.width(), height: $domElement.height()};
                        if (console) {
                            console.debug("resize hint '%s' for iframe '%s' is %o", resizeHintSelector, iframeId, resizeHint);
                        }
                    }
                }
            }
            catch(e) {
                if (console) {
                    console.error("error locating iframe %s resize hint, skipping resizing", iframeId);
                    console.error("exception %o", e);
                }
                $iframe.data('resizingDisabled', true);
            }
        }
        else {
            if (console) {
                console.info("no resize hint found for iframe '%s'", iframeId);
            }
        }
        return resizeHint;
    };

})(jQuery);
